#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>

#define COM	"/dev/ttyUSB0"
#define DEV_ADDR 			41
#define MODBUS_REQ_CODE			0x03
#define MODBUS_SET_CODE			0x06
#define MODBUS_REQ_LEN			8
#define MODBUS_REQ_INTERVAL		5			//seconds
#define RX_BUF_SIZE			32
#define MODBUS_CMD_LEN	8
#define MODBUS_REG_X				0x0004
#define MODBUS_REG_RELAY1		0x000E
#define MODBUS_REG_RELAY2		0x000F
#define MODBUS_REG_RELAY3		0x0010
#define MODBUS_REG_RELAY4		0x0011
#define MODBUS_REG_NUM			0x000A
#define RELAY_ON_CODE				0x0001
#define RELAY_OFF_CODE			0x0000
#define RELAY_ON_CODE_ALL		0xAAAA
#define RELAY_OFF_CODE_ALL	0xBBBB

typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;

void byte_order_reverse(uint8_t * data, uint8_t size)
{
	uint8_t x;
	if (data == NULL || size <= 0) return;
	for (int i = 0; i < size /2; i++)
	{
		x = data[i];
		data[i] = data[size - i - 1];
		data[size - i - 1] = x;
	}
}

typedef union
{
	uint16_t v16;
	uint8_t v8[2];
}modbus_data_t;


typedef struct __attribute__((packed))
{
	float x;
	float y;
	float z;
	float t1;
	uint16_t t2;
	uint16_t h;
}modbus_sensor_value_t;

typedef struct __attribute__((packed))
{
	float	x_value;
	float y_value;
	float z_value;
	float t_ds18b20;
	int16_t t_dht11;
	uint16_t humidity;
}modbus_sensor_t;

typedef struct __attribute__((packed))
{
	uint8_t dev_addr;
	uint8_t func_code;
	uint8_t data_len;
	modbus_sensor_value_t data;
	uint16_t crc16;
}modbus_req_resp_t;

typedef struct __attribute__((packed))
{
	uint8_t dev_addr;
	uint8_t func_code;
	uint16_t reg_addr;
	uint16_t data;
	uint16_t crc16;
}modbus_command_t;

int fd;
pthread_t tid_tx, tid_rx;
modbus_req_resp_t req_resp =
{
	.dev_addr = DEV_ADDR,
	.func_code = MODBUS_REQ_CODE,
	.data_len = sizeof(modbus_sensor_t),
	.crc16 = 0x0000,
};

float gen_float(float min, float max)
{
    /* rand() / RAND_MAX 产生 [0,1] 区间的浮点数 */
    float scale = (float)rand() / (float)RAND_MAX;
    return min + scale * (max - min);
}

unsigned short int gen_uint16(unsigned short int min,
                              unsigned short int max)
{
    /* 合法性保护：若 min > max 则交换 */
    if (min > max) {
        unsigned short int tmp = min;
        min = max;
        max = tmp;
    }

    /* RAND_MAX 至少为 32767，足以覆盖 0~65535；但我们仍作通用处理 */
    uint32_t range = (uint32_t)max - (uint32_t)min + 1u;

    /* 获得一个 0..RAND_MAX 的随机数并映射到 0..range-1 */
    uint32_t r = (uint32_t)rand();          /* 0 .. RAND_MAX */
    r = r % range;                          /* 0 .. range-1  */

    return (unsigned short int)(min + r);   /* 映射到 [min, max] */
}

void modbus_response(uint8_t * cmd_buf)
{
	modbus_command_t * p_cmd;
	uint16_t reg_addr, data;
	p_cmd = (modbus_command_t *)cmd_buf;
	reg_addr = p_cmd->reg_addr;
	data = p_cmd->data;
	byte_order_reverse((uint8_t *)&reg_addr, sizeof(uint16_t));
	byte_order_reverse((uint8_t *)&data, sizeof(uint16_t));
	if (p_cmd->func_code == MODBUS_REQ_CODE && reg_addr == MODBUS_REG_X && data == MODBUS_REG_NUM)
	{
		req_resp.data.x = gen_float(-0.2, 0.2);
		req_resp.data.y = gen_float(-0.2, 0.2);
		req_resp.data.z = gen_float(0.8, 1.2);
		req_resp.data.t1 = gen_float(20.0, 35.0);
		req_resp.data.t2 = req_resp.data.t1;
		req_resp.data.h = gen_uint16(20, 80);
		byte_order_reverse((uint8_t *)&(req_resp.data.x), sizeof(float));
		byte_order_reverse((uint8_t *)&(req_resp.data.y), sizeof(float));
		byte_order_reverse((uint8_t *)&(req_resp.data.z), sizeof(float));
		byte_order_reverse((uint8_t *)&(req_resp.data.t1), sizeof(float));
		byte_order_reverse((uint8_t *)&(req_resp.data.t2), sizeof(uint16_t));
		byte_order_reverse((uint8_t *)&(req_resp.data.h), sizeof(uint16_t));
		write(fd, (uint8_t *)&req_resp, sizeof(req_resp));
	}
	else if (p_cmd->func_code == MODBUS_SET_CODE && reg_addr >= MODBUS_REG_RELAY1 && reg_addr <= MODBUS_REG_RELAY4)
	{
		printf("***** ");
		if (data == RELAY_ON_CODE)
		{
			printf("Turn on relay%d", reg_addr - MODBUS_REG_RELAY1 + 1);
		}
		else if (data == RELAY_OFF_CODE)
		{
			printf("Turn off relay%d", reg_addr - MODBUS_REG_RELAY1 + 1);
		}
		else if (data == RELAY_ON_CODE_ALL)
		{
			printf("Turn on relay%d", reg_addr - MODBUS_REG_RELAY1 + 1);
			for (uint16_t reg = reg_addr + 1; reg <= MODBUS_REG_RELAY4; reg++)
			{
				printf(" relay%d", reg - MODBUS_REG_RELAY1 + 1);
			}
		}
		else if (data == RELAY_OFF_CODE_ALL)
		{
			printf("Turn off relay%d", reg_addr - MODBUS_REG_RELAY1 + 1);
			for (uint16_t reg = reg_addr + 1; reg <= MODBUS_REG_RELAY4; reg++)
			{
				printf(" relay%d", reg - MODBUS_REG_RELAY1 + 1);
			}
		}
		printf(" *****\n");
		write(fd, cmd_buf, MODBUS_CMD_LEN);
	}
}

void crc_calc(uint8_t * data, uint8_t len, uint8_t * crc_hb, uint8_t * crc_lb)
{
	*crc_hb = 0;
	*crc_lb = 0;
}

/* 在数据缓冲区buf（大小为size）中查找是否有数据b，如果有，返回其位置，否则返回-1 */
int find_byte(uint8_t * buf, uint8_t size, uint8_t b)
{
	int i;
	for (i = 0; i < size; i++)
	{
		if (buf[i] == b) break;
	}
	if (i < size)
	{
		return i;
	}
	else
	{
		return -1;
	}
}

void * com_rx_thread(void *arg)
{
	int len;			//串口数据缓冲区里的数据长度
	int n, pos;
	unsigned char rx_buf[RX_BUF_SIZE];//串口数据缓冲区
	while (1)
	{
		len = 0;
		while (1)
		{
			n = read(fd, rx_buf + len, RX_BUF_SIZE - 1);
			if (n > 0)
			{
				len += n;
				while (1)
				{
					if (rx_buf[0] != DEV_ADDR)	//检查收到的数据第1个字节是不是设备地址	
					{
						pos = find_byte(rx_buf, len, DEV_ADDR);
						if (pos >= 0)
						{
							memcpy(rx_buf, rx_buf + pos, len - pos);	//找到modbus响应的第1个字节，把从这个位置开始的modbus响应移动到rx_buf数组起始位置（方便后续解析modbus命令）
							len -= pos;
						}
						else
						{
							len = 0;	//没有找到modbus的第1个字节，清空缓冲区
							break;
						}
					}
					if (len < 3)
					{
						break;	//modbus应答帧只收到前2个字节，继续接收
					}
					else
					{
						if (rx_buf[1] != MODBUS_REQ_CODE && rx_buf[1] != MODBUS_SET_CODE)
						{
							memcpy(rx_buf, rx_buf + 1, len - 1);
							len--;
							continue;	//功能码不是合法的应答帧，删除头，继续下一轮解析
						}
						if (len >= MODBUS_CMD_LEN)	//接收完一个modbus应答帧
						{
							printf("rx modbus command: ");
							for (int i = 0; i < len; i++)
							{
								printf("%02X ", rx_buf[i]);
							}
							printf("\n");
							modbus_response(rx_buf);
							len -= MODBUS_CMD_LEN;
							if (len != 0)	//应答帧后面还有数据待分析
							{
								memcpy(rx_buf, rx_buf + MODBUS_CMD_LEN, len);	//删除已处理的应答数据
								continue;
							}
						}
						break;
					}
				}
			}
		}
	}
}

void on_sigint(int sig)
{
//	pthread_cancel(tid_tx);
	pthread_cancel(tid_rx);
	if (fd > 0)
	{
		close(fd);
	}
	printf("bye\n");
	exit(0);
}

int main(int argc, char * argv[])
{
	struct termios newtio;
	const char * com;
	if (argc == 1)
	{
		com = COM;
	}
	else
	{
		com = argv[1];
	}

	fd = open(com, O_RDWR | O_NOCTTY | O_NONBLOCK);
	if (fd == -1)
	{
		perror("open error");
		exit(0);
	}
	else
	{
		printf("%s opend!\n", argv[1]);
	}

	signal(SIGINT, on_sigint);
	newtio.c_cflag |= CLOCAL | CREAD;
	newtio.c_cflag |= CS8 | B115200;
	newtio.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
	newtio.c_oflag &= ~OPOST;
	newtio.c_cc[VMIN] = 0;
	newtio.c_cc[VTIME] = 5;
	cfmakeraw(&newtio);
	tcflush(fd, TCIFLUSH);
	tcsetattr(fd, TCSANOW, &newtio);

	srand((unsigned)time(NULL));
//	pthread_create(&tid_tx, NULL, com_tx_thread, NULL);
	pthread_create(&tid_rx, NULL, com_rx_thread, NULL);
//	pthread_join(tid_tx, NULL);
	pthread_join(tid_rx, NULL);
	close(fd);
	return 0;
}
